#include "myecpoint.h"
#include "qdebug.h"
#include <openssl/err.h>
///////////////////////////构造函数/////////////////////////////////
MyECPoint::MyECPoint(EC_GROUP* group) {
    m_group = std::shared_ptr<EC_GROUP>(group, [](EC_GROUP* group){
        EC_GROUP_free(group); });
    m_point = EC_POINT_new(group);
}

MyECPoint::MyECPoint(std::shared_ptr<EC_GROUP> mpGroup) {
    this->m_group = mpGroup;
    this->m_point = EC_POINT_new(mpGroup.get());
}

MyECPoint::MyECPoint(int group_nid)
{
    m_group = std::shared_ptr<EC_GROUP>(EC_GROUP_new_by_curve_name(group_nid), [](EC_GROUP* group){
        EC_GROUP_free(group); });
    m_point = EC_POINT_new(m_group.get());
}

MyECPoint::MyECPoint(const MyECPoint& other){
    this->m_group = other.m_group;
    this->m_point = EC_POINT_new(m_group.get());
    EC_POINT_copy(this->m_point, other.m_point);
}

MyECPoint::MyECPoint(MyECPoint &&other)
{
    this->m_group = other.m_group;
    this->m_point = EC_POINT_new(m_group.get());
    EC_POINT_copy(this->m_point, other.m_point);
}

MyECPoint::MyECPoint(EC_POINT* point, std::shared_ptr<EC_GROUP> group) {
    m_point = point;
    m_group = group;
}
///////////////////////////构造函数/////////////////////////////////


///////////////////////////设置点//////////////////////////////////
bool MyECPoint::setPoint(EC_POINT* point, bool copy)
{
    BN_CTX* ctx = BN_CTX_new();
    auto ret = EC_POINT_is_on_curve(m_group.get(), point, ctx);
    BN_CTX_free(ctx);
    if (ret!=1)
    {
        return false;
    }
    if (copy)
    {
        return EC_POINT_copy(this->m_point, point) == 1;
    }
    this->m_point = point;
    return true;
}

bool MyECPoint::setPoint(MyBIGNUM x, MyBIGNUM y)
{
    return EC_POINT_set_affine_coordinates(m_group.get(), m_point, x.m_num, y.m_num, nullptr);
}

bool MyECPoint::setPointFromOct(uchar *buf, int len)
{
    return EC_POINT_oct2point(m_group.get(),m_point,buf,len,nullptr);
}

bool MyECPoint::setPointFromRand() {
    BIGNUM* order = BN_new();
    EC_GROUP_get_order(m_group.get(), order,nullptr);
    int ret=BN_rand(order, BN_num_bits(order), 0, 0);
    if (!ret) goto err;
    ret=EC_POINT_mul(m_group.get(),m_point, order,nullptr,nullptr,nullptr);
    if (!ret) goto err;
    BN_free(order);
    return true;
err:
    BN_free(order);
    return false;
}

bool MyECPoint::setPointFromHex(std::string hex)
{
    return EC_POINT_hex2point(m_group.get(),hex.c_str(),m_point,nullptr);
}

MyECPoint& MyECPoint::operator=(const MyECPoint& other) {
    this->m_group = other.m_group;
    this->m_point = EC_POINT_new(m_group.get());
    EC_POINT_copy(this->m_point, other.m_point);
    return *this;
}
///////////////////////////设置点//////////////////////////////////


//////////////////////////运算符重载///////////////////////////////
MyECPoint MyECPoint::operator+(const MyECPoint& oth) {
    EC_POINT* tem = EC_POINT_new(m_group.get());
    BN_CTX* ctx = BN_CTX_new();
    if (EC_POINT_add(m_group.get(), tem, m_point, oth.m_point, ctx) == 0) {
        BN_CTX_free(ctx);
        EC_POINT_free(tem);
        return MyECPoint(nullptr, nullptr);
    }
    BN_CTX_free(ctx);
    return MyECPoint(tem, m_group);
}

MyECPoint MyECPoint::operator-(const MyECPoint& other)
{
    EC_POINT* rem = EC_POINT_new(m_group.get());
    EC_POINT_copy(rem, other.m_point);
    BN_CTX* ctx = BN_CTX_new();
    EC_POINT_invert(other.m_group.get(), rem, ctx);
    EC_POINT_add(m_group.get(), rem,m_point,rem,ctx);
    BN_CTX_free(ctx);
    return MyECPoint(rem, other.m_group);
}

MyECPoint MyECPoint::operator*(const MyBIGNUM& a) {
    EC_POINT* rem = EC_POINT_new(m_group.get());
    BN_CTX* ctx=BN_CTX_new();
    int ret=EC_POINT_mul(m_group.get(), rem,a.m_num, m_point,nullptr,ctx);
    BN_CTX_free(ctx);
    if(!ret)
    {
        qDebug()<<"EC_POINT_mul";
        ERR_print_errors_fp(stderr);
    }
    return MyECPoint(rem,this->m_group);
}

MyECPoint operator*(const MyBIGNUM &a,const MyECPoint& point){
    EC_POINT* rem = EC_POINT_new(point.m_group.get());
    BN_CTX* ctx=BN_CTX_new();
    EC_POINT_mul(point.m_group.get(), rem,nullptr, point.m_point,a.getNum(),ctx);
    return MyECPoint(rem,point.m_group);
}
////////////////////////////运算符重载///////////////////////////////


MyECPoint::~MyECPoint() {
    if (m_point) {
        EC_POINT_free(m_point);
    }
}
//取逆
MyECPoint operator-(const MyECPoint& point)
{
    EC_POINT* rem = EC_POINT_new(point.m_group.get());
    EC_POINT_copy(rem, point.m_point);
    BN_CTX* ctx = BN_CTX_new();
    EC_POINT_invert(point.m_group.get(), rem,ctx);
    BN_CTX_free(ctx);
    return MyECPoint(rem, point.m_group);
}

// 打印
std::ostream& operator<<(std::ostream& os, const MyECPoint& dt){
    BIGNUM* x = BN_new(), * y = BN_new();
    int ret=EC_POINT_get_affine_coordinates(dt.m_group.get(), dt.m_point, x, y, nullptr);
    if(ret!=1){
        return os;
    }
    char* s1=BN_bn2dec(x);
    char* s2= BN_bn2dec(y);
    BN_free(x);BN_free(y);
    os << "(" << s1 << "," << s2 << ")";
    OPENSSL_free(s1);OPENSSL_free(s2);
    return os;
}
//获取群信息
bool MyECPoint::getCurve(const MyBIGNUM& p,const MyBIGNUM& a,const MyBIGNUM& b){
    return EC_GROUP_get_curve(m_group.get(),p.m_num,a.m_num,b.m_num,nullptr)==1;
}

unsigned char *MyECPoint::toOct(size_t& len)
{
    unsigned char* ret=new unsigned char[33];
    len=EC_POINT_point2oct(m_group.get(),m_point,POINT_CONVERSION_COMPRESSED,ret,33,nullptr);
    if(len<=0){
        return nullptr;
    }
    return ret;
}
std::shared_ptr<unsigned char> MyECPoint::toBuf(size_t &len)
{
    unsigned char* ret=nullptr;
    len=EC_POINT_point2buf(m_group.get(),m_point,POINT_CONVERSION_COMPRESSED,&ret,nullptr);
    if(len<=0){
        return nullptr;
    }
    return std::shared_ptr<unsigned char>(
        ret,
        [](unsigned char* c)
        {
            if(c){OPENSSL_free(c);}
        });
}
std::string MyECPoint::toBuf()
{
    unsigned char* ret=new unsigned char[33];
    size_t len=EC_POINT_point2oct(m_group.get(),m_point,POINT_CONVERSION_COMPRESSED,ret,33,nullptr);
    if(len<=0){
        return std::string();
    }
    return std::string((char*)ret,len);
}
